﻿using DDDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DDDB
{
    //线程安全封装
    public class DataBase_Async
    {
        Database db;
        System.Collections.Concurrent.ConcurrentQueue<SendCmd> sendcmdqueue;
        System.Collections.Concurrent.ConcurrentQueue<ExistCmd> existcmdqueue;
        System.Collections.Concurrent.ConcurrentQueue<GetCmd> getcmdqueue;
        class SendCmd
        {
            public int tag = 0;
            public byte[] key;
            public byte[] value;
        }
        class ExistCmd
        {
            public int tag = 0;
            public byte[] key;
            public DDDB.DataPos result;
        }

        class GetCmd
        {
            public int tag = 0;
            public byte[] key;//key ==null then use pos
            public DDDB.DataPos pos;
            public byte[] result;
        }

        void DBThread()
        {
            while (true)
            {
                int busy = 0;
                {
                    if (sendcmdqueue.TryDequeue(out SendCmd cmd))
                    {
                        busy++;
                        db.Put(cmd.key, cmd.value);
                        cmd.tag = 1;
                    }
                }
                {
                    if (existcmdqueue.TryDequeue(out ExistCmd ecmd))
                    {
                        busy++;
                        db.Exist(ecmd.key, out ecmd.result);
                        ecmd.tag = 1;
                    }
                }
                {
                    if (getcmdqueue.TryDequeue(out GetCmd cmd))
                    {
                        busy++;
                        if (cmd.key != null)
                        {
                            cmd.result = db.Get(cmd.key);
                        }
                        else
                        {
                            cmd.result = db.GetByPos(cmd.pos);
                        }
                        cmd.tag = 1;
                    }
                }
                if (busy == 0)
                {
                    System.Threading.Thread.Sleep(1);
                }
            }
        }
        public async Task Init(string path)
        {
            bool initdbstat = false;
            ThreadPool.QueueUserWorkItem((s) =>
            {
                db = new Database(path);
                initdbstat = true;
            });

            while (!initdbstat)
            {
                await Task.Delay(100);
            }

            sendcmdqueue = new System.Collections.Concurrent.ConcurrentQueue<SendCmd>();
            existcmdqueue = new System.Collections.Concurrent.ConcurrentQueue<ExistCmd>();
            getcmdqueue = new System.Collections.Concurrent.ConcurrentQueue<GetCmd>();
            System.Threading.Thread thread = new Thread(DBThread);
            thread.IsBackground = true;
            thread.Start();
        }

        public void Close()
        {
            db.Dispose();
        }
        public void Put(byte[] key, byte[] data)
        {
            var cmd = new SendCmd { key = key, value = data };
            sendcmdqueue.Enqueue(cmd);
          
        }
        public bool HaveQueue
        {
            get
            {
                return sendcmdqueue.Count > 0 || existcmdqueue.Count > 0 || getcmdqueue.Count > 0;
            }
        }
        public async Task<DDDB.DataPos> Exist(byte[] key)
        {
            var cmd = new ExistCmd();
            existcmdqueue.Enqueue(cmd);
            while (cmd.tag == 0)
            {
                await Task.Delay(1);
            }
            return cmd.result;
        }
        public async Task<byte[]> Get(DDDB.DataPos pos)
        {
            var cmd = new GetCmd();
            cmd.pos = pos;
            getcmdqueue.Enqueue(cmd);
            while (cmd.tag == 0)
            {
                await Task.Delay(1);
            }
            return cmd.result;
        }
        public async Task<byte[]> Get(byte[] key)
        {
            var cmd = new GetCmd();
            cmd.key = key;
            getcmdqueue.Enqueue(cmd);
            while (cmd.tag == 0)
            {
                await Task.Delay(1);
            }
            return cmd.result;
        }
    }
}
